/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2008 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id: header 252479 2008-02-07 19:39:50Z iliaa $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_main.h"
#include "php_theme.h"
#include "ext/standard/md5.h"
#include "ext/standard/info.h"
#include "SAPI.h"

#include <zend_interfaces.h>

/* If you declare any globals in php_theme.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(theme)
*/

/* True global resources - no need for thread safety here */
static char *ht_rdir = "";
static zend_class_entry* theme_class_entry;
static zend_object_handlers theme_object_handlers;

/* {{{ arginfo */
ZEND_BEGIN_ARG_INFO(arginfo_theme_assign, 0)
	ZEND_ARG_INFO(0, item)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_import, 0)
	ZEND_ARG_INFO(0, node)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_exists, 0)
	ZEND_ARG_INFO(0, node)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_caching, 0)
	ZEND_ARG_INFO(0, nsec)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_display, 0)
	ZEND_ARG_INFO(0, node)
	ZEND_ARG_INFO(0, stpl)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_themedir, 0)
	ZEND_ARG_INFO(0, real)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_version, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_construct, 0)
	ZEND_ARG_INFO(0, stag)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_theme_destruct, 0)
ZEND_END_ARG_INFO()
/* }}} */

/* {{{ theme_functions[]
 *
 * Every user visible function must have an entry in theme_functions[].
 */
const zend_function_entry theme_functions[] = {
	PHP_ME(theme, assign, arginfo_theme_assign, ZEND_ACC_PUBLIC)
	PHP_ME(theme, import, arginfo_theme_import, ZEND_ACC_PUBLIC)
	PHP_ME(theme, exists, arginfo_theme_exists, ZEND_ACC_PUBLIC)
	PHP_ME(theme, caching, arginfo_theme_caching, ZEND_ACC_PUBLIC)
	PHP_ME(theme, display, arginfo_theme_display, ZEND_ACC_PUBLIC)
	PHP_ME(theme, themedir, arginfo_theme_themedir, ZEND_ACC_PUBLIC)
	PHP_ME(theme, version, arginfo_theme_version, ZEND_ACC_PUBLIC)
	PHP_ME(theme, __construct, arginfo_theme_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
	PHP_ME(theme, __destruct, arginfo_theme_destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
	//PHP_FE(theme, NULL)
	{NULL, NULL, NULL}	/* Must be the last line in theme_functions[] */
};
/* }}} */

/* {{{ theme_module_entry
 */
zend_module_entry theme_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
	STANDARD_MODULE_HEADER,
#endif
	"theme",
	theme_functions,
	PHP_MINIT(theme),
	PHP_MSHUTDOWN(theme),
	PHP_RINIT(theme),		/* Replace with NULL if there's nothing to do at request start */
	PHP_RSHUTDOWN(theme),		/* Replace with NULL if there's nothing to do at request end */
	PHP_MINFO(theme),
#if ZEND_MODULE_API_NO >= 20010901
	PHP_THEME_VERSION, /* Replace with version number for your extension */
#endif
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_THEME
ZEND_GET_MODULE(theme)
#endif

/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("theme.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_theme_globals, theme_globals)
    STD_PHP_INI_ENTRY("theme.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_theme_globals, theme_globals)
PHP_INI_END()
*/
/* }}} */

/* {{{ php_theme_init_globals
 */
/* Uncomment this function if you have INI entries
static void php_theme_init_globals(zend_theme_globals *theme_globals)
{
	theme_globals->global_value = 0;
	theme_globals->global_string = NULL;
}
*/
/* }}} */

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(theme)
{
	/* If you have INI entries, uncomment these lines 
	REGISTER_INI_ENTRIES();char md5str[33];
	PHP_MD5_CTX context;
	unsigned char digest[16];
	*/
	zend_class_entry class_entry;
	INIT_CLASS_ENTRY(class_entry, "theme", theme_functions);
	theme_class_entry = zend_register_internal_class(&class_entry TSRMLS_CC);

	zend_declare_property_string(theme_class_entry, ZEND_STRL("dir"), "", ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_string(theme_class_entry, ZEND_STRL("name"), "", ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_string(theme_class_entry, ZEND_STRL("cache"), "", ZEND_ACC_PRIVATE TSRMLS_CC);
	zend_declare_property_long(theme_class_entry, ZEND_STRL("caching"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_long(theme_class_entry, ZEND_STRL("errno"), 1, ZEND_ACC_PRIVATE TSRMLS_CC);
	zend_declare_property_null(theme_class_entry, ZEND_STRL("items"), ZEND_ACC_PRIVATE TSRMLS_CC);
 
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(theme)
{
	/* uncomment this line if you have INI entries
	UNREGISTER_INI_ENTRIES();
	*/
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(theme)
{
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
 */
PHP_RSHUTDOWN_FUNCTION(theme)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(theme)
{
	php_info_print_table_start();
	php_info_print_table_row(2, "theme support", "enabled");
	php_info_print_table_row(2, "theme version", PHP_THEME_VERSION);
	php_info_print_table_end();

	/* Remove comments if you have entries in php.ini
	DISPLAY_INI_ENTRIES();
	*/
}
/* }}} */


/* Remove the following function when you have succesfully modified config.m4
   so that your module can be compiled into PHP, it exists only for testing
   purposes. */

PHP_METHOD(theme, assign) /* {{{ */
{
	Bucket *p;
	zval *object, *items, *item, *value = NULL;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &item, &value) == FAILURE) {
		 WRONG_PARAM_COUNT;
	}

	object = getThis();
	items = zend_read_property(theme_class_entry, object, ZEND_STRL("items"), 0 TSRMLS_CC);
	if (Z_TYPE_P(items) != IS_ARRAY) {
		MAKE_STD_ZVAL(items);
		array_init(items);
	}

	if (Z_TYPE_P(item) == IS_ARRAY) {

		for (p = Z_ARRVAL_P(item)->pListHead; p != NULL; p = p->pListNext) {
			Z_ADDREF_PP((zval**)p->pData);

			if (p->nKeyLength == 0) {
				zend_hash_index_update(Z_ARRVAL_P(items), p->h, p->pData, sizeof(zval*), NULL);
			} else {
				zend_hash_quick_update(Z_ARRVAL_P(items), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
			}
		}
		zend_update_property(theme_class_entry, object, ZEND_STRL("items"), items TSRMLS_CC);
		RETURN_TRUE;
	}

	zval_addref_p(value);
	convert_to_string(item);
	if (zend_hash_update(Z_ARRVAL_P(items),Z_STRVAL_P(item), Z_STRLEN_P(item) + 1, (void**)&value, sizeof(zval*), NULL) != FAILURE) {
		zend_update_property(theme_class_entry, object, ZEND_STRL("items"), items TSRMLS_CC);
		RETURN_TRUE;
	}

	RETURN_FALSE;
}
/* }}} */

PHP_METHOD(theme, import) /* {{{ */
{
	zend_class_entry **ce;
	zend_function *constructor;
	char *node, *cmod, *path;
	int node_len;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &node, &node_len) == FAILURE) {
		 WRONG_PARAM_COUNT;
	}

	spprintf(&cmod, 0, "mod_%s", node);
	if (zend_lookup_class_ex(cmod, node_len + 4, 0, &ce TSRMLS_CC) == FAILURE) {
		zend_file_handle handle;

		spprintf(&path, MAXPATHLEN, "%smodels/%s.mod.php", ht_rdir, node);
		if (VCWD_ACCESS(path, F_OK) != 0){
			RETURN_FALSE;
		}
#ifdef PHP_WIN32
		if (zend_stream_open(path, &handle, (void ***)tsrm_ls) != SUCCESS) {
#else
		if (zend_stream_open(path, &handle TSRMLS_DC) != SUCCESS) {
#endif
			RETURN_FALSE;
		}
#ifdef PHP_WIN32
		php_execute_script(&handle, (void ***)tsrm_ls);
#else
		php_execute_script(&handle TSRMLS_DC);
#endif
		if (zend_lookup_class_ex(cmod, node_len + 4, 0, &ce TSRMLS_CC) == FAILURE) {
			RETURN_FALSE;
		}
	}
	
	object_init_ex(return_value, *ce);
	constructor = Z_OBJ_HT_P(return_value)->get_constructor(return_value TSRMLS_CC);
	if (constructor != NULL){
		zend_call_method_with_0_params(&return_value, *ce, &(*ce)->constructor, ZEND_CONSTRUCTOR_FUNC_NAME, NULL);
	}
}
/* }}} */

PHP_METHOD(theme, exists) /* {{{ */
{
	char *node, *path, *tdir;
	int node_len;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &node, &node_len) == FAILURE) {
		 WRONG_PARAM_COUNT;
	}

	tdir = ht_themedir(getThis(), 1);
	spprintf(&path, MAXPATHLEN, "%s%s.tpl.php", tdir, node);
	if (VCWD_ACCESS(path, F_OK) != 0) {
		RETURN_FALSE;
	}

	RETURN_TRUE;
}
/* }}} */

PHP_METHOD(theme, caching) /* {{{ */
{
	long nsec;
	char *unit;
	zval *zsec;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zsec) == FAILURE) {
		 WRONG_PARAM_COUNT;
	}

	if (Z_TYPE_P(zsec) == IS_STRING) {
		unit = Z_STRVAL_P(zsec) + Z_STRLEN_P(zsec) - 1;
		nsec = atol(estrndup(Z_STRVAL_P(zsec), Z_STRLEN_P(zsec) - 1));
		switch (*unit) {
			case 's': break;
			case 'h': nsec *= 3600; break;
			case 'd': nsec *= 86400; break;
			case 'm': nsec *= 2592000; break;
			case 'y': nsec *= 31536000; break;
			default : nsec *= 86400;
		}
	} else {
		convert_to_long_ex(&zsec);
		nsec = Z_LVAL_P(zsec);
	}

	zend_update_property_long(theme_class_entry, getThis(), ZEND_STRL("caching"), nsec TSRMLS_CC);
	zval_ptr_dtor(&zsec);
}
/* }}} */

PHP_METHOD(theme, display) /* {{{ */
{
	char *node, *stpl = "";
	int node_len, stpl_len;
	char *path, *tdir;
	zval *object, *skindir, *items;
	zend_file_handle handle;

	zval **entry;
	HashPosition hpos;
	ulong num_key;
	char *key_val;
	uint key_len;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
		"s|s", &node, &node_len, &stpl, &stpl_len) == FAILURE)
	{
		 WRONG_PARAM_COUNT;
	}

	object = getThis();
	tdir = ht_themedir(object, 1);

	spprintf(&path, MAXPATHLEN, "%s%s.tpl.php", tdir, node);
	if ((VCWD_ACCESS(path, F_OK) != 0) && (ZEND_NUM_ARGS() > 1)) {
		spprintf(&path, MAXPATHLEN, "%s%s.tpl.php", tdir, stpl);
	}

	if (VCWD_ACCESS(path, F_OK) != 0){
		zend_error(E_ERROR, "Cannot find template file: %s%s.tpl.php", ht_themedir(object, 0), node);
	}

#ifdef PHP_WIN32
	if (zend_stream_open(path, &handle, (void ***)tsrm_ls) == SUCCESS) {
#else
	if (zend_stream_open(path, &handle TSRMLS_DC) == SUCCESS) {
#endif
		ALLOC_HASHTABLE(EG(active_symbol_table));
		zend_hash_init(EG(active_symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);

		MAKE_STD_ZVAL(skindir); 
		ZVAL_STRING(skindir, ht_themedir(object, 0), 1);
		ZEND_SET_SYMBOL(EG(active_symbol_table), "skindir", skindir);

		items = zend_read_property(theme_class_entry, object, ZEND_STRL("items"), 0 TSRMLS_CC);
		if (Z_TYPE_P(items) == IS_ARRAY) {

			zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(items), &hpos);
			while (zend_hash_get_current_data_ex(Z_ARRVAL_P(items), (void **)&entry, &hpos) == SUCCESS) {
				zval_add_ref(entry);

				switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(items), &key_val, &key_len, &num_key, 0, &hpos)) {
					case HASH_KEY_IS_LONG:
						zend_hash_index_update(EG(active_symbol_table), num_key, (void **) entry, sizeof(zval *), NULL);
						break;
					case HASH_KEY_IS_STRING:
						zend_hash_update(EG(active_symbol_table), key_val, key_len, (void **) entry, sizeof(zval *), NULL);
						break;
				}
				zend_hash_move_forward_ex(Z_ARRVAL_P(items), &hpos);
			}
		}

#ifdef PHP_WIN32
		php_execute_script(&handle, (void ***)tsrm_ls);
#else
		php_execute_script(&handle TSRMLS_DC);
#endif
		zend_update_property_long(theme_class_entry, object, ZEND_STRL("errno"), 0 TSRMLS_CC);
		zend_hash_init(EG(active_symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);

		return;
	}

	zend_error(E_ERROR, "Cannot find template file: %s.tpl.php", node);
}
/* }}} */

PHP_METHOD(theme, themedir) /* {{{ */
{
	zend_bool real = 1;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &real) == FAILURE) {
		 WRONG_PARAM_COUNT;
	}

	RETURN_STRING(ht_themedir(getThis(), real), 0);
}
/* }}} */

PHP_METHOD(theme, version) /* {{{ */
{
	RETURN_STRING(PHP_THEME_VERSION, 1);
}
/* }}} */

PHP_METHOD(theme, __construct) /* {{{ */
{
	int rdir_len;
	char *qstr = "";
	size_t qstr_len = 0;
	zval rdir, **quri;
	sapi_header_line ctr = {0};

	FILE *fp;
	char *file = NULL, *path = NULL;

	char *html, *head, *tdir = "";
	size_t html_len;
	zval *object, *items;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &qstr, &qstr_len) == FAILURE) {
		WRONG_PARAM_COUNT;
	}
	
	// defined(ROOTDIR)
	if(!zend_get_constant(ZEND_STRL("ROOTDIR"), &rdir TSRMLS_CC)){
		zend_error(E_ERROR, "Undefined the constant ROOTDIR");
	}
	ht_rdir = Z_STRVAL(rdir);
	rdir_len = Z_STRLEN(rdir);
	if(ht_rdir[rdir_len-1] != '/' && ht_rdir[rdir_len-1] != '\\') {
		ht_rdir[rdir_len] = '/';
		ht_rdir[rdir_len + 1] = '\0';
	}

	// ob_start()
	php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC);

	// header('content-type:text/html; charset=utf-8');
	ctr.line = "content-type:text/html; charset=utf-8";
	ctr.line_len = strlen(ctr.line);
	sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);

	// get qstr
	zend_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC);
	if (qstr_len == 0){
		if (zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]),
			ZEND_STRS("REQUEST_URI"), (void **) &quri) == SUCCESS)
		{
			qstr_len = Z_STRLEN_PP(quri);
			qstr = Z_STRVAL_PP(quri);
		}
	}

	// find cache file
	file = ht_md5((unsigned char *) qstr, qstr_len);
	spprintf(&path, MAXPATHLEN, "%scaches/html/%s.cache", ht_rdir, file);

	if (fp = VCWD_FOPEN(path, "rb")) {
		head = (char *) emalloc(sizeof(char) * 10);
		fread(head, 1, 10, fp);
		if (atol(estrndup(head, 10)) > (long)time(NULL)) {
			html = (char *) emalloc(sizeof(char) * 1024);
			while(!feof(fp)){
				html_len = fread(html, 1, 1024, fp);
				zend_write(html, html_len);
			}
			efree(html);
			efree(head);
			zend_bailout();
			return;
		}
		efree(head);
		fclose(fp);
	}

	// update propertys
	MAKE_STD_ZVAL(items);
	array_init(items);

	spprintf(&tdir, MAXPATHLEN, "%sthemes/", ht_rdir, file);

	object = getThis();
	zend_update_property_string(theme_class_entry, object, ZEND_STRL("dir"), tdir TSRMLS_CC);
	zend_update_property_string(theme_class_entry, object, ZEND_STRL("name"), "default" TSRMLS_CC);
	zend_update_property_string(theme_class_entry, object, ZEND_STRL("cache"), path TSRMLS_CC);
	zend_update_property(theme_class_entry, object, ZEND_STRL("items"), items TSRMLS_CC);

}
/* }}} */

PHP_METHOD(theme, __destruct) /* {{{ */
{
	FILE *fp;
	zval html;
	char head[10];
	zval *object, *path, *errn, *cach;

	if (ZEND_NUM_ARGS() != 0) {
		WRONG_PARAM_COUNT;
	}

	object = getThis();
	// read propertys
	path = zend_read_property(theme_class_entry, object, ZEND_STRL("cache"), 0 TSRMLS_CC);
	errn = zend_read_property(theme_class_entry, object, ZEND_STRL("errno"), 0 TSRMLS_CC);
	cach = zend_read_property(theme_class_entry, object, ZEND_STRL("caching"), 0 TSRMLS_CC);
	
	if (Z_LVAL_P(errn) == 0 && Z_LVAL_P(cach) > 0) {

		gcvt((long)time(NULL) + Z_LVAL_P(cach), 10, head);
		// get buffer
		if (php_ob_get_buffer(&html TSRMLS_CC)!=FAILURE) {
			// write cache
			if(fp = VCWD_FOPEN(Z_STRVAL_P(path), "wb")){
				fwrite(&head, sizeof(head), 1, fp);
				fwrite(Z_STRVAL(html), Z_STRLEN(html), 1, fp);
				fclose(fp);
			}
		}
		
	}

	php_end_ob_buffer(1, 0 TSRMLS_CC);
}
/* }}} */

// ht_themedir
static char *ht_themedir(zval *object, zend_bool real) /* {{{ */
{
	char *retv;
	zval *tdir, *name;

	TSRMLS_FETCH();

	tdir = zend_read_property(theme_class_entry, object, ZEND_STRL("dir"), 0 TSRMLS_CC);
	name = zend_read_property(theme_class_entry, object, ZEND_STRL("name"), 0 TSRMLS_CC);

	spprintf(&retv, 0, "%s%s/", Z_STRVAL_P(tdir), Z_STRVAL_P(name));
	if (real == 0) {
		spprintf(&retv, 0, "%s%s/", Z_STRVAL_P(tdir) + strlen(ht_rdir), Z_STRVAL_P(name));
	}

	return retv;

}
/* }}} */

// ht_md5
static char *ht_md5(const void *data, size_t size) /* {{{ */
{
	PHP_MD5_CTX context;
	static char md5str[33];
	static unsigned char digest[16];

	PHP_MD5Init(&context);
	PHP_MD5Update(&context, data, size);
	PHP_MD5Final(digest, &context);
	make_digest(md5str, digest);

	return md5str;
}
/* }}} */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
